import serial
import math
import os
import serial.tools.list_ports
import re
from collections import Counter
import random
import numpy as np
import time
# import xlrd
# import xlwt
from sklearn import linear_model
from sklearn.preprocessing import PolynomialFeatures
# from predict import predict_model
import requests
import json
import socket,base64,hashlib
from deviceConnect import submitMeasureData

# 各个串口变量，windows系统
frontPort = 'com5'
backPort = 'com8'
laserFrontPort = 'com10'
laserBackPort = 'com11'
controlerPort = 'com9'
# 各个串口变量，linux系统
# frontPort = 'com27'
# backPort = 'com2'
# laserFrontPort = 'com5'
# laserBackPort = 'com26'
# controlerPort = 'com8'

# 框架宽度
frameworkDis = 948
laserFrameworkDis = 840

# 毫米波传感器端口连接
ser1 = serial.Serial(frontPort, 115200, timeout=0.5)
ser2 = serial.Serial(backPort, 115200, timeout=0.5)
# 激光传感器端口连接
ser3 = serial.Serial(laserFrontPort, 9600, timeout=0.5)
ser4 = serial.Serial(laserBackPort, 9600, timeout=0.5)
# 接收控制器发送的开始测量信号
ser = serial.Serial(controlerPort, 9600, timeout=0.5)

# 最后一个参数控制是否使用激光数据过滤毫米波测量值
def getData(ser1, ser2, setRange, limit1, limit2, useFilter = True):
    print('measureRange is from ' + str(setRange[0]) + 'mm to ' + str(setRange[1]) + 'mm, limit1 and limit2 is ' + str(limit1) + ',' + str(limit2))
    w = []
    d1Arr = []
    d2Arr = []
    p1 = []
    p2 = []
    leftLimit = setRange[0]
    rightLimit = setRange[1]

    # 清除串口缓存数据
    ser1.reset_input_buffer()
    ser2.reset_input_buffer()

    for i in range(20):
        try:
            s = str(ser1.readline())
            s = s.split(',')
            d = s[0]
            p = s[1]
            d = d.replace("b'Distance=  ", '')
            p = p.replace("peak= ",'')
            p = p.replace("peak=",'')
            p = p.replace("Peak=",'')
            p = p.replace("\\n'",'')
            d1 = int(d)
            if d1 > limit1 | (not useFilter):
                d1Arr.append(d1)
                p1.append(p)

            s = str(ser2.readline())
            s = s.split(',')
            d = s[0]
            p = s[1]
            d = d.replace("b'Distance=  ", '')
            p = p.replace("peak= ",'')
            p = p.replace("peak=",'')
            p = p.replace("Peak=",'')
            p = p.replace("\\n'",'')
            d2 = int(d)
            if d2 > limit2 | (not useFilter):
                d2Arr.append(d2)
                p2.append(p)

            w.append(frameworkDis - d1 - d2)
        except ValueError:
            continue
        except IndexError:
            continue
    
    # 筛选合规值   
    print('beforeFilter:', w)
    filterW = []
    for j in range(len(w)):
      if (w[j] > leftLimit and w[j] < rightLimit) or (not useFilter):
        filterW.append(w[j])

    afterSigmaFilter = sigmaFilter(filterW)
    print('afterSigmaFilter:', afterSigmaFilter)

    if len(afterSigmaFilter) > 0:
        # 测量值按出现频率取加权平均
        # r = Counter(a).most_common(1)[0][0]
        counter = Counter(afterSigmaFilter).most_common()
        print('counter', counter)
        elements = []
        weights = []
        for key, value in counter:
            elements.append(key)
            weights.append(value)
        measureValue = np.average(elements, weights = weights)
        print('measureValue:', measureValue)
        return measureValue, elements
    else:
        print('thers is some wrong with 红外测距，是不是穿了黑色衣服')

def getDataOnce (ser1, ser2):
    s = str(ser1.readline())
    s = s.split(',')
    d = s[0]
    d = d.replace("b'Distance=  ", '')
    d1 = int(d)

    s = str(ser2.readline())
    s = s.split(',')
    d = s[0]
    d = d.replace("b'Distance=  ", '')
    d2 = int(d)

    return frameworkDis - d1 - d2

def sigmaFilter (arr):
    arr = np.array(arr)
    diffArr = arr - np.mean(arr)
    sigma = ((np.sum(diffArr ** 2) / diffArr.shape[0])) ** 0.5
    filterArr = []
    for i in range(len(arr)):
        if abs(diffArr[i]) < sigma:
            filterArr.append(arr[i])
    return filterArr

def getLaserWidth(ser1, ser2):
    w = []
    d1Arr = []
    d2Arr = []

    # 清除串口缓存数据
    ser1.reset_input_buffer()
    ser2.reset_input_buffer()

    for i in range(10):
        try:
            d = str(ser1.readline())
            d = d.replace("b'", '')
            d = d.replace("mm\\r\\n'", '')
            # print(d)
            d1 = int(d)
            d1Arr.append(d1)

            d = str(ser2.readline())
            d = d.replace("b'", '')
            d = d.replace("mm\\r\\n'", '')
            d2 = int(d)
            d2Arr.append(d2)

            w.append(laserFrameworkDis - d1 - d2)
        # except:
        #     continue
        except ValueError:
            continue
        except PermissionError:
            continue

    # 测量值按出现频率取加权平均
    # r = Counter(a).most_common(1)[0][0]
    try:
        print('Laser counter', w)
        counter = Counter(w).most_common()
        d1Value = Counter(d1Arr).most_common(1)[0][0]
        d2Value = Counter(d2Arr).most_common(1)[0][0]
        print('d1:', d1Value, 'd2:', d2Value)
        elements = []
        weights = []
        for key, value in counter:
            elements.append(key)
            weights.append(value)
        measureValue = np.average(elements, weights=weights)
        print('Laser measureValue:', measureValue)
        return int(measureValue), d1Value, d2Value
    except IndexError:
        print('Laser error')
        return 0, 0, 0

def measureStart (index, count, data):
    heightStr = ['', '臀', '腰', '胸', '肩膀']
    countStr = [0, '厚', '宽']

    ser.write(('recv' + index).encode())
    # 测量程序运行
    # 获取测量范围
    laserWidth, d1, d2 = getLaserWidth(ser3, ser4)

    # valueRange = [laserWidth - 30, laserWidth + 30]
    valueRange = [150, laserWidth + 100]

    # 如果获取雷达过程出错，则不适用过滤
    if laserWidth == 0:
        # 获取测量值，最后参数是否采用激光过滤
        testData = getData(ser1, ser2, valueRange, d1, d2, False)
    else:
        # 获取测量值，最后参数是否采用激光过滤
        testData = getData(ser1, ser2, valueRange, d1, d2, False)

    if not testData:
        data[heightStr[int(index)] + countStr[count]] = 'measure error'
    else:
        # 判断是否可能是凹陷的部位
        if index == '2':
            coef = shapeEstimate(trendData[int(index) - 1])
            if coef < 0:
                print('可能凹陷部位', min(testData[1]))
                data[heightStr[int(index)] + countStr[count]] = min(testData[1])
            else:
                data[heightStr[int(index)] + countStr[count]] = testData[0]
        elif (index == '3') and (count == 2):
            print('胸宽位置')
            data[heightStr[int(index)] + countStr[count]] = min(testData[1])
        else:
            data[heightStr[int(index)] + countStr[count]] = testData[0]

    # 延时的目的是为了recv 和 done指令不让控制器同时接收，变成recvdone
    time.sleep(1)
    ser.write(('done' + index).encode())
    recv = ''
    while recv != ('over' + index):
        print(recv)
        recv = str(ser.readline())
        recv = recv.replace("b'", '')
        recv = recv.replace("\\r\\n'", '')


def shapeEstimate (meaList):
    x = []
    y = []
    for i in range(len(meaList)):
        y.append([meaList[i]])
        x.append([i])
    model = linear_model.LinearRegression()
    model.fit(x, y)
    # 线性模型的系数
    return model.coef_[0]
    print('斜率：' + str(coef))


# def predictMeasureValue (meaList):
#     x = []
#     y = []
#     length = len(meaList)
#     for i in range(length):
#         y.append([meaList[i]])
#         x.append([i])
#     # 特征构造
#     poly_reg = PolynomialFeatures(degree=2)
#     x = poly_reg.fit_transform(x)
#     model = linear_model.LinearRegression()
#     model.fit(x, y)
#     x = [[length], [length + 1]]
#     # 用特征构造数据进行预测
#     prediceValue = model.predict(poly_reg.fit_transform(x))

#     value1, value2 = prediceValue[0][0], prediceValue[1][0]
#     return [value1, value2]
    
def addWeiduPredict(data):
    heightStr = ['', '臀', '腰', '胸', '肩膀']
    countStr = ['', '厚', '宽', '围']
    predicgStr = ['', 'hips', 'waist', 'chest']
    
    for i in range(1, 4):
      data[heightStr[i] + countStr[3]] = predict_model(predicgStr[i], data[heightStr[i] + countStr[2]], data[heightStr[i] + countStr[1]])


def reset_device():
    ser = serial.Serial(controlerPort, 9600, timeout=0.5)
    ser.write(('reset').encode())


def run():
    # 高度标记
    index = 1
    # 宽厚标记
    count = 1

    # 体重变量
    weight = 0

    # 是否移动扫描过程中的标志变量
    isScan = False

    # 记录测量数据的字典
    measureData = dict()
    measureData['臀宽'] = 300
    measureData['臀厚'] = 200
    measureData['腰宽'] = 260
    measureData['腰厚'] = 160
    measureData['胸宽'] = 280
    measureData['胸厚'] = 180
    measureData['身高'] = 165

    # 移动过程扫描数据
    scanData = []
    # 移动过程扫描人体轮廓趋势
    trendData = []

    # 阻塞等待开始信息，信号包括身高信息
    height = input("输入被测者的身高（cm）")
    measureData['身高'] = height

    print("start Script!")
    ser.write(("begin" + str(height)).encode())
    while True:
        # 读串口数据格式化处理
        recv = str(ser.readline())
        recv = recv.replace("b'", '')
        recv = recv.replace("\\r\\n'", '')
        # print(recv)
        # 下面为根据不同的串口指令执行相应的操作
        if recv[0:6] == 'weight':
            weight = int(recv.replace("weight", ""))
            # print(weight)
            measureData['体重'] = weight

        elif (recv == 'done1') | (recv == 'done2') | (recv == 'done3') | (recv == 'done4'):
            isScan = False
            print(scanData)
            trendData.append(scanData)
            scanData = []
            index = recv[4]
            measureStart(str(index), count, measureData)

        elif recv == 'startMove':
            isScan = True

        elif isScan:
            # 还没到达位置的移动过程中前不断获取被测人的厚度信息
            # 清除串口缓存数据
            ser1.reset_input_buffer()
            ser2.reset_input_buffer()
            try:
                scanData.append(getDataOnce(ser1, ser2))
            except ValueError:
                continue
            except IndexError:
                continue


        if (index == '3') and (count == 1):
            print("Measure over")
            print(measureData)
            # predictData = []
            # for i in range(len(trendData)):
            #     predictData.append(predictMeasureValue(trendData[i]))
            # print('对应的预测值为：')
            # print(predictData)
            count = count + 1
            temp = input('按回车开始测量宽度\n')
            ser.write("next".encode())

        if index == '4':
            print("Measure over")
            # 根据宽厚度添加预测围度
            # addWeiduPredict(measureData)
            print(measureData)
            # predictData = []
            # for i in range(len(trendData)):
            #     predictData.append(predictMeasureValue(trendData[i]))
            # print('对应的预测值为：')
            # print(predictData)
            issubmit = input("是否上传这次测量数据，上传则输入y:  ")
            if issubmit == 'y':
                submitMeasureData(measureData)
            break


if __name__ == "__main__":
    # test()
    # run()
    #/////////////
    while True:
        issubmit = input("回车开始测量")
        # # 获取测量范围
        laserWidth, d1, d2 = getLaserWidth(ser3, ser4)

        # valueRange = [laserWidth - 30, laserWidth + 30]
        valueRange = [100, laserWidth + 100]

        # 如果获取雷达过程出错，则不适用过滤
        if laserWidth == 0:
            # 获取测量值，最后参数是否采用激光过滤
            testData = getData(ser1, ser2, valueRange, d1, d2, False)
        else:
            # 获取测量值，最后参数是否采用激光过滤
            testData = getData(ser1, ser2, valueRange, d1, d2, False)
    #//////////////

